﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Shell;
using SABnzbdNET.Commands;
using SABnzbdNET.Events;
using SABnzbdNET.Model;
using SABnzbdNET.ServiceAgents;

namespace SABnzbdNET.ViewModels
{
    public class ServerInfoViewModel : BaseViewModel
    {
        #region Fields and properties

        private DelegateCommand launchBrowserCommand;
        private DelegateCommand<Model.Job> startCommand; 
        private DelegateCommand<Model.Job> pauseCommand;
        private DelegateCommand<Model.Job> deleteCommand;
        private DelegateCommand<ProcessingCommandParameter> processingCommand;
        private DelegateCommand<PriorityCommandParameter> priorityCommand;
        private ObservableCollection<string> categories;
        private readonly IEventAggregator eventAggregator;
        private readonly System.Windows.Threading.DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer();
        private ServerInfo serverInfo;
        private Job firstJob;
        private string downloadSpeed;
        private string totalProgress; 
        private int totalProgressPercent;
        private string timeLeft;
        private string diskSpace;

        public ICommand LaunchBrowserCommand
        {
            get { return launchBrowserCommand ?? (launchBrowserCommand = new DelegateCommand(LaunchBrowser)); }
        }
        public ICommand StartCommand
        {
            get { return startCommand ?? (startCommand = new DelegateCommand<Model.Job>(StartJob)); }
        }
        public ICommand PauseCommand
        {
            get { return pauseCommand ?? (pauseCommand = new DelegateCommand<Model.Job>(PauseJob)); }
        }
        public ICommand DeleteCommand
        {
            get { return deleteCommand ?? (deleteCommand = new DelegateCommand<Model.Job>(DeleteJob)); }
        }
        public ICommand ProcessingCommand
        {
            get { return processingCommand ?? (processingCommand = new DelegateCommand<ProcessingCommandParameter>(ChangeProcessing)); }
        }
        public ICommand PriorityCommand
        {
            get { return priorityCommand ?? (priorityCommand = new DelegateCommand<PriorityCommandParameter>(ChangePriority)); }
        }
        public ObservableCollection<string> Categories
        {
            get { return categories; }
            set { categories = value; OnPropertyChanged("Categories"); }
        }
        public ServerInfo ServerInfo
        {
            get { return serverInfo; }
            set
            {
                serverInfo = value;
                OnPropertyChanged("ServerInfo");
            }
        }
        public Job FirstJob
        {
            get { return firstJob; }
            set
            {
                firstJob = value;
                OnPropertyChanged("FirstJob");
            }
        }
        public string DownloadSpeed
        {
            get { return downloadSpeed; }
            set
            {
                downloadSpeed = value;
                OnPropertyChanged("DownloadSpeed");
            }
        }
        public string TotalProgress
        {
            get { return totalProgress; }
            set
            {
                totalProgress = value;
                OnPropertyChanged("TotalProgress");
            }
        }
        public int TotalProgressPercent
        {
            get { return totalProgressPercent; }
            set
            {
                totalProgressPercent = value;
                OnPropertyChanged("TotalProgressPercent");
            }
        }
        public string TimeLeft
        {
            get { return timeLeft; }
            set
            {
                timeLeft = value;
                OnPropertyChanged("TimeLeft");
            }
        }
        public string DiskSpace
        {
            get { return diskSpace; }
            set
            {
                diskSpace = value;
                OnPropertyChanged("DiskSpace");
            }
        }
        #endregion

        public ServerInfoViewModel()
        {
            eventAggregator = EventAggregatorProvider.Instance;
            eventAggregator.Subscribe<UpdateServerInfoMessage>(message => GetInfo());
            timer.Tick += TimerTick;
            timer.Start();
        }

        private void TimerTick(object sender, EventArgs e)
        {
            GetInfo();
        }

        private void GetInfo()
        {
            timer.Interval = new TimeSpan(0, Properties.Settings.Default.TimerInterval, 0);
            timer.IsEnabled = true;
            Mouse.OverrideCursor = Cursors.AppStarting;
            SetStatusMessage("Updating", "Updating");
            new ServerAgent().GetInfo(DeployResult);
        }

        private void DeployResult(ServerInfo info, string errorMessage)
        {
            if (info == null)
            {
                ServerInfo.Current = null;
                ServerInfo = null;
                FirstJob = null;
                SetTaskbarProgressInfo(info, 0);

                SetStatusMessage("Error updating: " + errorMessage, "Error updating:\n" + errorMessage);
                eventAggregator.Publish(new ShowTrayBallonMessage
                                          {
                                            Title = "Error updating",
                                            Text = "There was an error updating the server info:\n" + errorMessage,
                                            Icon = Hardcodet.Wpf.TaskbarNotification.BalloonIcon.Error
                                          });

                DownloadSpeed = string.Empty;
                TotalProgress = string.Empty;
                TotalProgressPercent = 0;
                TimeLeft = string.Empty;
                DiskSpace = string.Empty;
            }
            else
            {
                ServerInfo.Current = info;
                SetCategories();
                ServerInfo = info;
                FirstJob = (info.Jobs != null ? info.Jobs.FirstOrDefault() : null);
                DownloadSpeed = info.KbPerSec.ToString("N2") + " kb/s";
                if (info.TotalSize > 0)
                {
                    TotalProgress = string.Format("{0} MB of {1} MB left", info.TotalSizeRemaining, info.TotalSize);
                    TotalProgressPercent = (int)((info.TotalSize - info.TotalSizeRemaining) / info.TotalSize * 100);
                }
                else
                {
                    TotalProgress = string.Empty;
                    TotalProgressPercent = 0;
                }
                TimeLeft = string.Format("{0} (Eta: {1})", info.TimeLeft, info.Eta);
                DiskSpace = string.Format("{0} GB of {1} GB free", info.FreeDiskSpace, info.TotalDiskSpace);

                if (FirstJob != null)
                {
                    SetTaskbarProgressInfo(info, ((double)TotalProgressPercent / 100));
                    SetStatusMessage("Last updated " + DateTime.Now,
                        string.Format("Status: {0}{1}Current title: {2}{3}Speed: {4}{5}Total progress: {6}", 
                        ServerInfo.Status, Environment.NewLine, FirstJob.Title, Environment.NewLine, 
                        DownloadSpeed, Environment.NewLine, TotalProgress + "%"));
                }
                else
                {
                    SetTaskbarProgressInfo(info, 0);
                    SetStatusMessage("Last updated " + DateTime.Now, string.Format("Status: {0}", ServerInfo.Status));
                }
            }
            Mouse.OverrideCursor = null;
        }

        private void SetCategories()
        {
            var temp = new ObservableCollection<string>();
            if (ServerInfo.Current.Categories != null)
            {
                foreach (var item in ServerInfo.Current.Categories)
                {
                    temp.Add(item);
                }
            }
            Categories = temp;
        }

        private static void SetTaskbarProgressInfo(ServerInfo info, double progress)
        {
            if (Application.Current != null && Application.Current.MainWindow != null && Application.Current.MainWindow.TaskbarItemInfo != null)
            {
                if (info == null)
                    Application.Current.MainWindow.TaskbarItemInfo.ProgressState = TaskbarItemProgressState.Indeterminate;
                else
                    Application.Current.MainWindow.TaskbarItemInfo.ProgressState = info.Paused ? TaskbarItemProgressState.Paused : TaskbarItemProgressState.Normal;
                Application.Current.MainWindow.TaskbarItemInfo.ProgressValue = progress;
            }
        }

        private void SetStatusMessage(string statusMessage, string notifyIconTooltip)
        {
            eventAggregator.Publish(new UpdateStatusInfoMessage
                                      {
                                        StatusMessage = statusMessage,
                                        NotifyIconMessage = notifyIconTooltip
                                      });
        }

        private static void LaunchBrowser()
        {
            Process.Start(new ProcessStartInfo(Properties.Settings.Default.ServerPath));
        }

        private void StartJob(Model.Job job)
        {
            new ServerAgent().StartDownload(job.Id, GetInfo, ShowError);
        }

        private void PauseJob(Model.Job job)
        {
            new ServerAgent().PauseDownload(job.Id, GetInfo, ShowError);
        }

        private void DeleteJob(Model.Job job)
        {
            MessageBoxResult result = MessageBox.Show(string.Format("Are you sure you want to delete download of {0}?", job.Title),
                "Delete download", MessageBoxButton.YesNo);
            if (result == MessageBoxResult.Yes)
                new ServerAgent().DeleteDownload(job.Id, GetInfo, ShowError);
        }

        private void ChangeProcessing(ProcessingCommandParameter parameter)
        {
            new ServerAgent().ChangeProcessingForDownload(parameter.Job.Id, parameter.Processing, GetInfo, ShowError);
        }

        private void ChangePriority(PriorityCommandParameter parameter)
        {
            new ServerAgent().ChangePriorityForDownload(parameter.Job.Id, parameter.Priority, GetInfo, ShowError);
        }

        private static void ShowError(string errorMessage)
        {
            MessageBox.Show(errorMessage, "Error");
        }
    }
}
